package com.wang.jmonkey.gateway.config;

import com.wang.jmonkey.common.support.yml.TransformYmlFactory;
import com.wang.jmonkey.gateway.handler.auth.authentication.AuthenticationFailureHandler;
import com.wang.jmonkey.gateway.handler.auth.authentication.AuthenticationManager;
import com.wang.jmonkey.gateway.handler.auth.authentication.AuthenticationSuccessHandler;
import com.wang.jmonkey.gateway.handler.auth.authorization.AccessDeniedHandler;
import com.wang.jmonkey.gateway.handler.auth.authorization.AuthenticationEntryPointHandler;
import com.wang.jmonkey.gateway.handler.auth.authorization.AuthorizationManager;
import com.wang.jmonkey.gateway.handler.auth.converter.TokenAuthenticationConverter;
import com.wang.jmonkey.gateway.property.SecurityProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;

/**
 * @Description 网关配置
 * @Author HeJiawang
 * @Date 2021/5/31 12:48
 */
@PropertySource(value= {SecurityProperty.SECURITY_PROPERTY_PATH} , factory = TransformYmlFactory.class)
@EnableConfigurationProperties(SecurityProperty.class)
@Configuration
public class GatewayConfig {

    @Autowired
    private AuthorizationManager authorizationManager;

    @Autowired
    private SecurityProperty securityProperty;

    @Autowired
    private TokenStore tokenStore;

    @Bean
    public TokenStore redisTokenStore(RedisConnectionFactory redisConnectionFactory) {
        RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
        return tokenStore;
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {

        ReactiveAuthenticationManager tokenAuthenticationManager = new AuthenticationManager(tokenStore);
        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(tokenAuthenticationManager);

        authenticationWebFilter.setAuthenticationFailureHandler(new AuthenticationFailureHandler()); //认证失败
        authenticationWebFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler()); //认证成功

        TokenAuthenticationConverter tokenAuthenticationConverter = new TokenAuthenticationConverter();
        tokenAuthenticationConverter.setAllowUriQueryParameter(true);
        authenticationWebFilter.setServerAuthenticationConverter(tokenAuthenticationConverter);

        http.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);

        ServerHttpSecurity.AuthorizeExchangeSpec authorizeExchange = http.authorizeExchange();
        authorizeExchange
                .matchers(EndpointRequest.toAnyEndpoint()).permitAll()
                .pathMatchers(
                        securityProperty.getIgnoredUrls().toArray(new String[securityProperty.getIgnoredUrls().size()])
                ).permitAll()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange().access(authorizationManager)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler())
                .authenticationEntryPoint(new AuthenticationEntryPointHandler())
                .and()
                .headers().frameOptions().disable()
                .and()
                .httpBasic().disable().csrf().disable();

        return http.build();
    }
}
